package com.zhang.IO.NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * Nio服务器端
 */
public class NioServer {
    public void start() throws IOException {
        /**
         * 1.创建Selector
         */
       Selector selector= Selector.open();
        /**
         * 2.通过ServerSocketChannel创建Channel通道
         */
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        /**
         * 3.为Channel通道绑定端口
         */
        serverSocketChannel.bind(new InetSocketAddress(8000));
        /**
         * 4.设置Channel为非阻塞模式
         */
        serverSocketChannel.configureBlocking(false);
        /**
         * 5.将Channel祖册到selector上监听连接事件
         */
        serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
        System.out.println("服务已启动！");
        /**
         * 6.无线循环等待接入的连接
         */
        for (;;)  //和while(true)一样
        {
            /**
             * 获取可以的Channel数量
             */
           int readyChannels= selector.select();//是一个阻塞方法，只有上面监听的就绪了才会返回

        if ( readyChannels==0) continue;
            /**
             * 获取可用的Channel集合，每次获取到都会放入到SelectedKeys中，
             * 所以下面需要删除掉已处理的
             */
        Set<SelectionKey> selectionKeys= selector.selectedKeys();
            Iterator iterator=selectionKeys.iterator();
            while (iterator.hasNext())
            {
                /**
                 * 处理Channel内容
                 */
                SelectionKey selectionKey= (SelectionKey) iterator.next();

                /**
                 * 移除Set中当前的SelectionKey
                  */
                iterator.remove();

                //7.根据状态调用业务方法
                /**
                 * 如果是接入事件
                 */
                if (selectionKey.isAcceptable()) {
                    acceptHandler(serverSocketChannel,selector);
                }
                /**
                 * 如果是可读事件
                 */
                 if (selectionKey.isReadable())
                 {
                     readHandler(selectionKey,selector);
                 }


            }
        }

    }

    /**
     * 广播给其他客户端
     */
    private  void broadCase(Selector selector,SocketChannel sourceChannel,String req)
    {
        /**
         * 获取到已接入的客户端Channer,
         * 值得注意的是这个keys方法记录的是已建立连接的客户端
         */
        Set<SelectionKey> selectionKeys=selector.keys();
        selectionKeys.forEach(selectionKey -> {
            Channel targetChannel=selectionKey.channel();
            if (targetChannel instanceof  SocketChannel
                    && targetChannel!=sourceChannel)
            {
                try{
                    //将消息发送到目标客户端
                    ((SocketChannel) targetChannel).write(Charset.forName("UTF-8").encode(req));
                }
                catch (IOException e) {
                    System.out.println(e);
                }
            }
        });
    }
    /**
     * 处理接入事件处理器
     */
 private void acceptHandler(ServerSocketChannel serverSocketChannel,
                            Selector selector) throws IOException {
     //如果是接入事件，创建SocketChannel
    SocketChannel socketChannel= serverSocketChannel.accept();
     //将SocketChannel设置为非阻塞工作模式
        socketChannel.configureBlocking(false);
     //将channel祖册到Selector上，监听可读事件
      socketChannel.register(selector,SelectionKey.OP_READ);
     //回复客户端提示信息
     socketChannel.write(Charset.forName("UTF-8").encode("您已进入聊天室"));
 }
    /**
     * 处理可读事件处理器
     */
    private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
       //从SelectionKey中获取已就绪的Channel
        SocketChannel socketChannel= (SocketChannel) selectionKey.channel();
        //创建buffer
        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);

        String request="";
        //循环读取客户端请求信息
        while(socketChannel.read(byteBuffer)>0)
        {
            //切换为buffer为读模式
         byteBuffer.flip();
            /**
             * 读取buffer中的内容
             */
          request+=Charset.forName("UTF-8").decode(byteBuffer);
        }
        //将channel再次注册到selector上，监听他的可读事件
        socketChannel.register(selector,SelectionKey.OP_READ);
        //将客户端发送的消息广播出来
        if (request.length()>0)
        {
            System.out.println("::  "+request);
            //广播给其他客户端
            broadCase(selector,socketChannel,request);
        }
    }

    public static void  main(String[] arg0) throws IOException {
      new NioServer().start();
    }
}
